/**
 * Copy right 2013 by Phan Hoàn Vũ
 * Email hoanvuphan@gmail.com
 */
package vn.universex;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;

import vn.universex.parser.PackageParser;

/**
 * ExecutorPoJo.
 * 
 * @goal run
 * @author Phan Hoan Vu
 */
public class ExecutorPoJo extends AbstractMojo {

    /**
     * @parameter expression="${sourceDirectories}"
     */
    private String sourceDirectories = "D:/projects/elca/CoIn/r2f/trunk/modules/r2f-service/src/main/java/org/msf/r2f/model";

    /**
     * @parameter expression="${outputDirectory}"
     */
    private String outputDirectory = "D:/temp";

    /**
     * @parameter expression="${resultClassSuffix}" default-value= "Properties"
     */
    private String resultClassSuffix = "Properties";

    /**
     * A data store for all package informations of the parsing process.
     */
    private Map<String, Set<String>> packageInfos = new HashMap<String, Set<String>>();;

    /**
     * {@inheritDoc}
     */
    public void execute() throws MojoExecutionException {
        getLog().info("Generating java code...");
        try {
            executeInternal();
            getLog().info("Generated successfully.");
        } catch (Throwable e) {
            getLog().error("Error when generate", e);
            throw new MojoExecutionException("Error when generate", e);
        }
    }

    private void executeInternal() {
        String[] sources = StringUtils.split(sourceDirectories);
        for (String source : sources) {
            parseDirectory(new File(StringUtils.replaceChars(StringUtils.stripEnd(source, " /\\"), "/\\",
                    File.separator)));
        }

    }

    /**
     * Parse all java file in the given directory.
     * 
     * @param dir
     *            input directory
     */
    private void parseDirectory(File dir) {
        if (!dir.exists()) {
            getLog().error(dir + " does not exists.");
            throw new IllegalArgumentException(dir + " does not exists.");
        }

        if (!dir.isDirectory()) {
            getLog().error(dir + " is not source directory.");
            throw new IllegalArgumentException(dir + " is not source directory.");
        }

        try {
            FileUtils.copyDirectory(new File(ExecutorPoJo.class.getResource("/java").getPath()), new File(
                    outputDirectory));
        } catch (IOException e) {
            getLog().error("Can not copy default class to output directory", e);
            throw new IllegalArgumentException("Can not copy default class to output directory", e);
        }

        // Visit all java files first time for rolling up
        visitAllJavaFiles(new FileFilter() {

            @Override
            public boolean accept(File javaFile) {
                new PackageParser().visit(javaFile, packageInfos);
                return false;
            }
        }, dir);

        // Visit all java files the second time for parsing and generating property class.
        visitAllJavaFiles(new FileFilter() {

            @Override
            public boolean accept(File javaFile) {
                new PlainTextJavaClassGenerator().generate(javaFile, new File(outputDirectory), resultClassSuffix,
                        packageInfos);
                return false;
            }
        }, dir);

    }

    /**
     * A recursive method that's used to visit all java class in the given directory.
     * 
     * @param callback
     *            handler method
     * @param dir
     *            input directory
     */
    private void visitAllJavaFiles(FileFilter callback, File dir) {
        File[] listFiles = dir.listFiles(new FileFilter() {

            @Override
            public boolean accept(File file) {
                return file.isDirectory() || file.getName().endsWith(".java");
            }
        });
        if (listFiles != null) {
            for (File element : listFiles) {
                if (element.isDirectory()) {
                    visitAllJavaFiles(callback, element);
                } else {
                    callback.accept(element);
                }
            }
        }
    }

}
